package Sentry;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;



class Sentry {

	static HashMap<String, String> config=new HashMap<String, String>();
	static HashMap<String,String[]> hostFail=new HashMap<String, String[]>();
	public static void main(String[] args) { 
		// legge il file file di configurazione
		config=XMLReader.get("/etc/sentry/config.xml","");

		// creazione del file log.txt se necessario
		File file = new File(config.get("log"));
		try {
			if (!file.exists()) {file.createNewFile();}
		}
		catch (IOException e) {
			System.out.println(TimeDate.get()+" Errore! Creazione log.txt non consentita");
			System.out.println(e.toString());
			System.exit(0);		
		}

		Log.put("Applicazione avviata");

		// legge il file host.xml e popola la matrice host
		HashMap <String,String[]> host=new HashMap<String, String[]>(); 
		HashMap<String, String> hostNodeXML = XMLReader.get(config.get("host"), "");
		
		//avvia il thread CheckHost
		CheckHost checkHost = new CheckHost();
		Thread t=new Thread(checkHost);
		t.start();
		
		Set<String> hostNodeSet = hostNodeXML.keySet();
		for (String iter : hostNodeSet) {
			String nodeArray[]=new String[4];
			HashMap<String, String> nodeXML=XMLReader.get(config.get("host"), iter);
			nodeArray[0]=nodeXML.get("ip"); //ip
			nodeArray[1]=nodeXML.get("port"); //porta
			nodeArray[2]=nodeXML.get("contact"); //contatto da richiamare in caso di evento
			nodeArray[3]="active"; // active viene controllato nel ciclo altrimenti "suspectdown"
			host.put(iter, nodeArray);
		}
		boolean alert=false;
		while (!alert) {
			String failArray[]=new String[5];
			int i=0;
			for (String hostIteration: hostNodeSet) {
				String nodeArray[]=host.get(hostIteration);
				if (nodeArray[3].equals("active" or "down")) {
					Ping ping=new Ping();
					i++;
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						Log.put("Errore di sistema durante Thread.sleep", e);
					}
					boolean reacheable = ping.start(nodeArray[0], nodeArray[1]);
					failArray[0]=nodeArray[0];
					failArray[1]=nodeArray[1];
					failArray[2]=nodeArray[2];
					failArray[4]=0;
					failArray[5]=0;
					if (nodeArray[3].equals("active")and !reacheable) {
						failArray[3]="suspectdown"
						Log.put("L'host "+hostIteration+" non ha risposto. Viene sottoposto a controllo");
					} else if (nodeArray[3].equals("down") and reacheable) {
						failArray[3]="suspectup"
						Log.put("L'host "+hostIteration+" ha risposto. Viene sottoposto a controllo");
					}
					hostFail.put(hostIteration, nodeArray);
				}
			}
			if (i==0) {
				alert=true;
			}
		}
		Log.put("Attenzione! Nessun host ha risposto al ping");
	}
}



class Log {	

	public static void put(String message) {
		boolean writeToLog=false;
		try {
			FileWriter logfile= new FileWriter(Sentry.config.get("log"), true);
			PrintWriter out=new PrintWriter(new BufferedWriter(logfile));
			out.println(TimeDate.get()+" - "+message);
			out.close();
			writeToLog=true;
		}
		catch (Exception e) {
			System.out.println(TimeDate.get()+" - Si è verificato un errore nella scrittura del file "+Sentry.config.get("log"));
			System.out.println(e.toString());
		}
		if (!writeToLog) {
			System.out.println(TimeDate.get()+" - "+message);
		}
	}
	public static void put(String message,Exception except) {
		boolean writeToLog=false;
		try {
			FileWriter logfile= new FileWriter(Sentry.config.get("log"), true);
			PrintWriter out=new PrintWriter(new BufferedWriter(logfile));
			out.println(TimeDate.get()+" - "+message);
			out.println(except.toString());
			out.close();
			writeToLog=true;
		}
		catch (Exception e) {
			System.out.println(TimeDate.get()+" - Si è verificato un errore nella scrittura del file "+Sentry.config.get("log"));
		}
		if (!writeToLog) {
			System.out.println(TimeDate.get()+" - "+message);
			System.out.println(except.toString());
		}
	}
}



class TimeDate {

	public static String get() {
		/* Questo metodo restituisce una stringa contenente la data corrente formattata 
		 * 
		 */
		Calendar calendar =new GregorianCalendar();
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
		String timeDate = sdf.format(calendar.getTime());
		return timeDate;
	}
}



class XMLReader {
	public static HashMap<String, String> get(String file,String node) {

		HashMap<String, String> xml=new HashMap<String, String>();

		try {
			DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
			DocumentBuilder parser = factory.newDocumentBuilder();
			Document document = parser.parse(new File(file));
			document.getDocumentElement().normalize();
			NodeList param = document.getDocumentElement().getChildNodes();
			if (!node.equals("")) {
				param=document.getElementsByTagName(node).item(0).getChildNodes();
			}
			for (int i=1;i<param.getLength();i++) {
				Node n=param.item(i);
				if (n.getNodeType()==Node.ELEMENT_NODE){
					xml.put(param.item(i).getNodeName(),param.item(i).getTextContent());
				}
			}		
		}
		catch (IOException e) {
			Log.put(" Errore! File "+file+" non trovato",e);
		}
		catch (Exception e) {
			Log.put(" Errore! File "+file+" corrotto",e);
		}
		return xml;
	}
}



class Ping {

	public String reason=null;

	public boolean start(String ip, String port) {
		Process nmap = null;
		if (port.isEmpty()) {
			try {
				nmap=Runtime.getRuntime().exec("nmap -sP -oX - "+ip);
			} catch (IOException e) {
				Log.put(" Errore! non è possibile avviare nmap",e);
				System.exit(0);
			}
			String state=null;
			String reason=null;
			try {
				DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
				DocumentBuilder parser = factory.newDocumentBuilder();
				Document document = parser.parse(nmap.getInputStream());
				document.getDocumentElement().normalize();
				NamedNodeMap status= document.getElementsByTagName("status").item(0).getAttributes();
				state = status.getNamedItem("state").getNodeValue();
				reason = status.getNamedItem("reason").getNodeValue();
			} catch (Exception e) {
				Log.put(" Errore! Funzionamento anomalo nmap o del parser DOM!",e);
				System.exit(0);
			}
			if (state.equalsIgnoreCase("up")) {
				this.reason=reason;
				return true;
			} else if (state.equalsIgnoreCase("down")) {
				this.reason=reason;
				return false;
			} else {
				Log.put("Attenzione! nmap ha restituito un parametro erreto, ping non effettuato");
			}
		} else {
			try {
				nmap=Runtime.getRuntime().exec("nmap -p"+port+" -oX - "+ip);
			} catch (IOException e) {
				Log.put(" Errore! nmap non trovato",e);
				System.exit(0);
			}
			String state=null;
			String reason=null;
			try {
				DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
				DocumentBuilder parser = factory.newDocumentBuilder();
				Document document = parser.parse(nmap.getInputStream());
				document.getDocumentElement().normalize();
				NamedNodeMap status= document.getElementsByTagName("status").item(0).getAttributes();
				state = status.getNamedItem("state").getNodeValue();
				reason = status.getNamedItem("reason").getNodeValue();
				if (state.equalsIgnoreCase("up")) {
					NamedNodeMap portState= document.getElementsByTagName("state").item(0).getAttributes();
					state = portState.getNamedItem("state").getNodeValue();
					reason = portState.getNamedItem("reason").getNodeValue();
					if (state.equalsIgnoreCase("closed")) {
						this.reason=reason;
						return false;
					}
					else {
						this.reason=reason+" "+state;
						return true;
					}
				} else if (state.equalsIgnoreCase("down")) {
					this.reason=reason;
					return false;
				} else {
					Log.put("Attenzione! nmap ha restituito un parametro erreto, ping non effettuato");
				}

			} catch (Exception e) {
				Log.put("Errore! Funzionamento anomalo nmap o del parser DOM!",e);
				System.exit(0);
			} 
		}
		return false;
	}	
}



class CheckHost implements Runnable {

	@Override
	public void run() {
		if (!Sentry.hostFail.isEmpty()) {
			Set<String> failedHost=Sentry.hostFail.keySet();
			for (String iter:failedHost) {
				
			}
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			Log.put("Errore di sistema durante Thread.sleep", e);
			e.printStackTrace();
		}
	}
}
